package com.xinghen.common.zookeeper.generator.impl;

import com.google.common.base.Preconditions;
import com.xinghen.common.zookeeper.generator.IdGenerator;

import java.util.Objects;

/**
 * @author xinghen
 * @ClassName: UniqueIdGenerator
 * @Package: com.xinghen.common.zookeeper.generator.impl
 * @Description 分布式唯一ID生成器
 * @create 2018-04-12 16:27
 */
public class UniqueIdGenerator implements IdGenerator {

    private static UniqueIdGenerator idGen;

    /**
     * 开始使用该算法的时间为: 2017-01-01 00:00:00
     */
    private static final long START_TIME = 1483200000000L;

    /**
     * worker id的bit数，最多支持8192个app和host的组合（即在N个服务器上每个服务器部署M个项目，总共部署N*M=8192）
     */
    private static final int APP_HOST_ID_BITS = 13;

    /**
     * 序列号，支持单节点最高1000*1024的并发
     */
    private final static int SEQUENCE_BITS = 10;

    /**
     * 最大的app host id，8091
     */
    private final static long MAX_APP_HOST_ID = ~(-1L << APP_HOST_ID_BITS);

    /**
     * 最大的序列号，1023
     */
    private final static long MAX_SEQUENCE = ~(-1L << SEQUENCE_BITS);

    /**
     * app host编号的移位
     */
    private final static long APP_HOST_ID_SHIFT = SEQUENCE_BITS;

    /**
     * 时间戳的移位
     */
    private final static long TIMESTAMP_LEFT_SHIFT = APP_HOST_ID_BITS + APP_HOST_ID_SHIFT;

    /**
     * 该项目的app host id，对应着为某台机器上的某个项目分配的serviceId（注意区分Span中的serviceId）
     */
    private long appHostId;

    /**
     * 上次生成ID的时间戳
     */
    private long lastTimestamp = -1L;

    /**
     * 当前毫秒生成的序列
     */
    private long sequence = 0L;

    public static UniqueIdGenerator getInstance(long appHostId) {
        if (Objects.isNull(idGen)) {
            synchronized (UniqueIdGenerator.class) {
                if (Objects.isNull(idGen)) {
                    idGen = new UniqueIdGenerator(appHostId);
                }
            }
        }
        return idGen;
    }

    private UniqueIdGenerator(long appHostId) {
        Preconditions.checkArgument(appHostId <= MAX_APP_HOST_ID,
                "appHostId已超过最大值" + MAX_APP_HOST_ID);
        this.appHostId = appHostId;
    }


    @Override
    public Long nextId() {
        return genUniqueId();
    }

    /**
     * 生成唯一id的具体实现
     *
     * @return
     */
    private synchronized long genUniqueId() {
        long current = System.currentTimeMillis();
        // 如果当前时间小于上一次ID生成的时间戳，说明系统时钟回退过，出现问题返回-1
        if (current < lastTimestamp) {
            return -1L;
        } else
            // 如果当前生成id的时间还是上次的时间，那么对sequence序列号进行+1
            if (current == lastTimestamp) {
                sequence = (sequence + 1) & MAX_SEQUENCE;
                // 当前毫秒生成的序列数已经大于最大值，那么阻塞到下一个毫秒再获取新的时间戳
                if (sequence == MAX_SEQUENCE) {
                    current = this.nextMs(lastTimestamp);
                }
            }
            // 当前的时间戳已经是下一个毫秒
            else {
                sequence = 0L;
            }
        // 更新上次生成id的时间戳
        lastTimestamp = current;
        // 进行移位操作生成int64的唯一ID
        return ((current - START_TIME) << TIMESTAMP_LEFT_SHIFT)
                | (this.appHostId << APP_HOST_ID_SHIFT)
                | sequence;
    }

    /**
     * 阻塞到下一个毫秒
     *
     * @param timeStamp
     * @return
     */
    private long nextMs(long timeStamp) {
        long current = System.currentTimeMillis();
        while (current <= timeStamp) {
            current = System.currentTimeMillis();
        }
        return current;
    }

    /**
     * 生成唯一ID
     *
     * @return
     */
    public static long generateId() {
        return UniqueIdGenerator.getInstance(IncrementIdGenerator.getServiceId()).nextId();
    }

}
